package il.ac.tau.cvws.scrabblesidekick.server.util.math;

import il.ac.tau.cvws.scrabblesidekick.shared.util.math.Vector;

public class Matrix
{
	private double x1, y1, z1;
	private double x2, y2, z2;
	private double x3, y3, z3;
	private double tx, ty, tz;
	
	public Matrix()
	{
		this(1.0, 0.0, 0.0,
			 0.0, 1.0, 0.0,
			 0.0, 0.0, 1.0,
			 0.0, 0.0, 0.0);
	}
	
	public Matrix(double x1, double y1, double z1, double x2, double y2, double z2,
			  	  double x3, double y3, double z3)
	{
		this(x1, y1, z1, x2, y2, z2, x3, y3, z3, 0.0, 0.0, 0.0);
	}
	
	public Matrix(double x1, double y1, double z1, double x2, double y2, double z2,
				  double x3, double y3, double z3, double tx, double ty, double tz)
	{
		this.x1 = x1;
		this.y1 = y1;
		this.z1 = z1;
		
		this.x2 = x2;
		this.y2 = y2;
		this.z2 = z2;
		
		this.x3 = x3;
		this.y3 = y3;
		this.z3 = z3;
		
		this.tx = tx;
		this.ty = ty;
		this.tz = tz;
	}
	
	public Matrix mul(Matrix m)
	{
		Matrix result = new Matrix();
		
		result.x1 = x1 * m.x1 + x2 * m.y1 + x3 * m.z1;
		result.y1 = y1 * m.x1 + y2 * m.y1 + y3 * m.z1;
		result.z1 = z1 * m.x1 + z2 * m.y1 + z3 * m.z1;
		
		result.x2 = x1 * m.x2 + x2 * m.y2 + x3 * m.z2;
		result.y2 = y1 * m.x2 + y2 * m.y2 + y3 * m.z2;
		result.z2 = z1 * m.x2 + z2 * m.y2 + z3 * m.z2;
		
		result.x3 = x1 * m.x3 + x2 * m.y3 + x3 * m.z3;
		result.y3 = y1 * m.x3 + y2 * m.y3 + y3 * m.z3;
		result.z3 = z1 * m.x3 + z2 * m.y3 + z3 * m.z3;
		
		result.tx = x1 * m.tx + x2 * m.ty + x3 * m.tz + tx;
		result.ty = y1 * m.tx + y2 * m.ty + y3 * m.tz + ty;
		result.tz = z1 * m.tx + z2 * m.ty + z3 * m.tz + tz;
		
		return result;
	}
	
	public Vector mul(Vector v)
	{
		Vector result = new Vector();
		
		result.setX(x1 * v.getX() + x2 * v.getY() + x3 * v.getZ() + tx);
		result.setY(y1 * v.getX() + y2 * v.getY() + y3 * v.getZ() + ty);
		result.setZ(z1 * v.getX() + z2 * v.getY() + z3 * v.getZ() + tz);
		
		return result;
	}
	
	public Matrix scale(double s)
	{
		Matrix result = new Matrix();
		
		result.x1 = s * x1;
		result.y1 = s * y1;
		result.z1 = s * z1;
		
		result.x2 = s * x2;
		result.y2 = s * y2;
		result.z2 = s * z2;
		
		result.x3 = s * x3;
		result.y3 = s * y3;
		result.z3 = s * z3;
		
		result.tx = s * tx;
		result.ty = s * ty;
		result.tz = s * tz;
		
		return result;
	}
	
	public double determinant()
	{
		double p = (x1 * y2 * z3) + (x2 * y3 * z1) + (x3 * y1 * z2);
		double n = (x1 * y3 * z2) + (x2 * y1 * z3) + (x3 * y2 * z1);
		
		return p - n;
	}
	
	public Matrix inverse()
	{
		Matrix result = new Matrix();
		
		double d = determinant();
		
		if (d == 0)
		{
			return null;
		}
		
		result.x1 = (y2 * z3 - y3 * z2) / d;
		result.y1 = (y3 * z1 - y1 * z3) / d;
		result.z1 = (y1 * z2 - y2 * z1) / d;
		
		result.x2 = (x3 * z2 - x2 * z3) / d;
		result.y2 = (x1 * z3 - x3 * z1) / d;
		result.z2 = (x2 * z1 - x1 * z2) / d;
		
		result.x3 = (x2 * y3 - x3 * y2) / d;
		result.y3 = (x3 * y1 - x1 * y3) / d;
		result.z3 = (x1 * y2 - x2 * y1) / d;
		
		result.tx = -(result.x1 * tx + result.x2 * ty + result.x3 * tz);
		result.ty = -(result.y1 * tx + result.y2 * ty + result.y3 * tz);
		result.tz = -(result.z1 * tx + result.z2 * ty + result.z3 * tz);
		
		return result;
	}
}
